package org.jeecg.modules.gis.util.mybatis;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.geotools.data.FeatureWriter;
import org.geotools.data.Transaction;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.geojson.feature.FeatureJSON;
import org.geotools.geojson.geom.GeometryJSON;
import org.geotools.geometry.jts.WKBReader;
import org.locationtech.jts.geom.Geometry;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.springframework.stereotype.Component;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.sql.Blob;
import java.sql.Struct;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Component
public class GeoJsonUtil {
    public JSONObject mapToFeatureDm(Map<String, Object> map) throws Exception {
        JSONObject featureJson = new JSONObject();
        JSONObject properties = new JSONObject();
        featureJson.put("type", "Feature");
        featureJson.put("properties", properties);
        JSONObject geometryJson = new JSONObject();

        for (Map.Entry<String, Object> entry : map.entrySet()) {
            if (entry.getKey().equalsIgnoreCase("geom")) {
                Object value = entry.getValue();
                if (value instanceof Struct) {
                    Struct struct = (Struct) value;
                    Object[] attrs = struct.getAttributes();

                    // attrs[1] 是 Blob 类型，存的是 WKB 二进制
                    Blob blob = (Blob) attrs[1];
                    byte[] wkb = blob.getBytes(1, (int) blob.length());

                    // 解析 WKB 到 Geometry
                    WKBReader wkbReader = new WKBReader();
                    Geometry geometry = wkbReader.read(wkb);

                    String type = geometry.getGeometryType();
                    geometryJson.put("type", type);

                    GeometryJSON geometryJSON = new GeometryJSON(7);
                    StringWriter sw = new StringWriter();
                    geometryJSON.write(geometry, sw);
                    JSONObject geoJson = JSONObject.parseObject(sw.toString());
                    geometryJson.put("coordinates", geoJson.get("coordinates"));
                    featureJson.put("geometry", geometryJson);
                }
            }
            else {
                properties.put(entry.getKey(), entry.getValue());
            }
        }

        return featureJson;
    }
    public JSONObject mapToFeatureMysql(Map<String, Object> map) throws Exception {
        JSONObject featureJson = new JSONObject();
        JSONObject properties = new JSONObject();
        featureJson.put("type", "Feature");
        featureJson.put("properties", properties);
        JSONObject geometryJson = new JSONObject();

        for (Map.Entry<String, Object> entry : map.entrySet()) {
            if (entry.getKey().equalsIgnoreCase("geom")) {
                Object value = entry.getValue();

                // MySQL 通常返回 byte[] 或 WKB 格式字符串
                byte[] wkb = null;

                if (value instanceof byte[]) {
                    wkb = (byte[]) value;
                } else if (value instanceof Blob) {
                    Blob blob = (Blob) value;
                    wkb = blob.getBytes(1, (int) blob.length());
                } else {
                    throw new IllegalArgumentException("MySQL 中 geom 字段类型不支持: " + value.getClass());
                }
                byte[] geomBytes = ByteBuffer.allocate(wkb.length - 4).order(ByteOrder.LITTLE_ENDIAN)
                        .put(wkb, 4, wkb.length - 4).array();
                WKBReader wkbReader = new WKBReader();
                Geometry geometry = wkbReader.read(geomBytes);
                String type = geometry.getGeometryType();
                geometryJson.put("type", type);

                GeometryJSON geometryJSON = new GeometryJSON(7);
                StringWriter sw = new StringWriter();
                geometryJSON.write(geometry, sw);
                JSONObject geoJson = JSONObject.parseObject(sw.toString());
                geometryJson.put("coordinates", geoJson.get("coordinates"));
                featureJson.put("geometry", geometryJson);
            } else {
                properties.put(entry.getKey(), entry.getValue());
            }
        }

        return featureJson;
    }

    public JSONObject mapToFeature(Map<String, Object> map,String dbType) throws Exception {
        switch (dbType.toLowerCase()) {
            case "mysql":
                return mapToFeatureMysql(map);
            case "postgresql":
                return null;
            case "oracle":
                return null;
            case "dm":
            case "dameng":
                return mapToFeatureDm(map);
            default:
                throw new UnsupportedOperationException("不支持的数据库类型: " + dbType);
        }
    }
    public JSONObject listMapToFeatureCollection(List<Map<String, Object> >list,String dbType) throws Exception {
        JSONObject featureCollectionJson = new JSONObject();
        JSONArray features = new JSONArray();
       for (Map<String, Object> map : list){
           features.add(mapToFeature(map,dbType));
       }
        featureCollectionJson.put("type", "FeatureCollection");
        featureCollectionJson.put("features", features);
        return featureCollectionJson;
    }
    public File exportGeoJsonToShpAndZip(String geoJsonStr, String fileBaseName) throws Exception {
        // 1. 基础目录（不删根目录）
        String exportRootDir = System.getProperty("java.io.tmpdir") + "/gisworkspace/exportdata";
        File rootDir = new File(exportRootDir);
        if (!rootDir.exists()) rootDir.mkdirs();

        // 2. 唯一子目录，避免冲突
        String uniqueDirName = fileBaseName + "_" + System.currentTimeMillis();
        File exportDir = new File(rootDir, uniqueDirName);
        exportDir.mkdirs();

        File zipFile = new File(rootDir, fileBaseName + ".zip");
        try {
            // 3. 写 GeoJSON 临时文件
            File geoJsonFile = new File(exportDir, "input.geojson");
            try (Writer writer = new FileWriter(geoJsonFile)) {
                writer.write(geoJsonStr);
            }

            // 4. 读取 GeoJSON
            FeatureJSON featureJSON = new FeatureJSON();
            SimpleFeatureCollection featureCollection;
            try (FileReader reader = new FileReader(geoJsonFile)) {
                featureCollection = (SimpleFeatureCollection) featureJSON.readFeatureCollection(reader);
            }

            // 5. 写 Shapefile
            File shpFile = new File(exportDir, fileBaseName + ".shp");
            ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
            Map<String, Serializable> params = new HashMap<>();
            params.put("url", shpFile.toURI().toURL());
            params.put("create spatial index", Boolean.TRUE);
            ShapefileDataStore dataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
            dataStore.setCharset(StandardCharsets.UTF_8);

            // 6. Schema + 写入
            SimpleFeatureType schema = featureCollection.getSchema();
            dataStore.createSchema(schema);
            try (
                    FeatureWriter<SimpleFeatureType, SimpleFeature> writer = dataStore.getFeatureWriterAppend(Transaction.AUTO_COMMIT);
                    SimpleFeatureIterator iterator = featureCollection.features()
            ) {
                while (iterator.hasNext()) {
                    SimpleFeature feature = iterator.next();
                    SimpleFeature newFeature = writer.next();
                    newFeature.setDefaultGeometry(feature.getDefaultGeometry());
                    for (AttributeDescriptor descriptor : schema.getAttributeDescriptors()) {
                        String name = descriptor.getLocalName();
                        if (!name.equals(schema.getGeometryDescriptor().getLocalName())) {
                            Object value = feature.getAttribute(name);
                            newFeature.setAttribute(name, value);
                        }
                    }
                    writer.write();
                }
            }

            // 7. 打包 ZIP（只包含 .shp/.shx/.dbf/.prj/.cpg 等）
            try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile))) {
                for (File file : Objects.requireNonNull(exportDir.listFiles())) {
                    if (!file.getName().equalsIgnoreCase("input.geojson")) {
                        try (FileInputStream fis = new FileInputStream(file)) {
                            zipOut.putNextEntry(new ZipEntry(file.getName()));
                            byte[] buffer = new byte[1024];
                            int length;
                            while ((length = fis.read(buffer)) >= 0) {
                                zipOut.write(buffer, 0, length);
                            }
                            zipOut.closeEntry();
                        }
                    }
                }
            }

            return zipFile;

        } finally {
            // 8. 删除临时目录（不删 zip）
            deleteDirectory(exportDir);
        }
    }


    // 递归删除目录
    public static void deleteDirectory(File dir) {
        if (dir.isDirectory()) {
            File[] files = dir.listFiles();
            if (files != null) {
                for (File file : files) {
                    deleteDirectory(file);
                }
            }
        }
        dir.delete();
    }
}
